﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web;
using System.Collections.Specialized;

namespace Roberts.MvcExtensions
{
    /// <summary>
    /// Returns JSONP when a callback is identified, otherwise returns regular JSON.
    /// You can also specify whether you want <c>DataContractJsonSerializer</c> serialization or to use the <c>JavaScriptSerializer</c>
    /// </summary>
    /// <typeparam name="T">Generic type</typeparam>
    public class JsonpResult<T> : ActionResult where T : class, new()
    {
      
        public const string DefaultJqueryCallbackParameter = "callback";
        private string callbackParameter;
        private bool useDataContract = false;
        private bool useDefaultCallback = true;
        
        #region Constructors

        public JsonpResult(T data, bool useDefaultCallbacks, bool useDataContract)
        {
            this.Data = data;
            this.useDefaultCallback = useDefaultCallbacks;
            this.useDataContract = useDataContract;
        }

        public JsonpResult(T data, string callbackParamter) : this(data, callbackParamter, false)
        {           
        }

        public JsonpResult(T data, string callbackParamter, bool useDataContract)
        {
            this.Data = data;
            this.callbackParameter = callbackParamter;
            this.useDefaultCallback = false;
        }

        public JsonpResult(T data) : this(data, true, false)
        {
        }

       

        #endregion Constructors

        #region Properties

        public Encoding ContentEncoding { get; set; }

        public string ContentType { get; set; }

        public T Data { get; set; }

        public JsonRequestBehavior JsonRequestBehavior { get; set; }

        public bool UseDataContractSerialization
        {
            get { return useDataContract; }
            set { useDataContract = value; }
        }

        #endregion Properties

        #region Methods

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            HttpResponseBase response = context.HttpContext.Response;

            if (!String.IsNullOrEmpty(ContentType))
            {
                response.ContentType = ContentType;
            }
            else
            {
                response.ContentType = "application/json";
            }

            if (ContentEncoding != null)
            {
                response.ContentEncoding = ContentEncoding;
            }

            if (Data != null)
            {

                HttpRequestBase request = context.HttpContext.Request;
                var jsonp =  IsJsonp(request.Params);
                if(jsonp.Item1)
                {
                    this.JsonRequestBehavior = JsonRequestBehavior.AllowGet; //this is new in MVC 2 otherwise an error will be thrown                    
                    response.Write(jsonp.Item2 + "(" + Data.ToJSON(useDataContract) + ")");                    
                }                
                else
                {
                    //normal json here
                    response.Write(Data.ToJSON(useDataContract));
                }

            }
        }

        private Tuple<bool, string> IsJsonp(NameValueCollection parameters)
        {                     
            if (useDefaultCallback && parameters[DefaultJqueryCallbackParameter] != null)
            {
                return new Tuple<bool, string>(true, parameters[DefaultJqueryCallbackParameter]);
            }
            else if(!string.IsNullOrEmpty(callbackParameter) && null !=  parameters[callbackParameter] ){
                return new Tuple<bool,string>(true, parameters[callbackParameter]);
            }
            return new Tuple<bool,string>(false, string.Empty);            
                
        }

        #endregion Methods
    }
}
